#include <screen.h>
#include <string.h>
#include <io.h>
#include <vsprintf.h>

// screen utils
// mostly stolen from sekiOS / Brians Kernel Tut

/* These define our textpointer, our background and foreground
*  colors (attributes), and x and y cursor coordinates */
static unsigned short *textmemptr;
static int attrib = 0x0F;
static int csr_x = 0, csr_y = 0;


/* Scrolls the screen */
static void scroll (void)
{
  unsigned blank, temp;

  /* A blank is defined as a space... we need to give it
   *  backcolor too */
  blank = 0x20 | (attrib << 8);

  /* Row 25 is the end, this means we need to scroll up */
  if (csr_y >= 25)
    {
      /* Move the current text chunk that makes up the screen
       *  back in the buffer by a line */
      temp = csr_y - 25 + 1;
      memcpy ((void *) textmemptr, (void *) (textmemptr + temp * 80),
	      (25 - temp) * 80 * 2);

      /* Finally, we set the chunk of memory that occupies
       *  the last line of text to our 'blank' character */
      memsetw (textmemptr + (25 - temp) * 80, blank, 80);
      csr_y = 25 - 1;
    }
}

/* Updates the hardware cursor */
static void move_csr (void)
{
  unsigned temp;

  temp = csr_y * 80 + csr_x;

  /* This sends a command to indicies 14 and 15 in the
   *  CRT Control Register of the VGA controller. These
   *  are the high and low bytes of the index that show
   *  where the hardware cursor is to be 'blinking'. */
  outportb (0x3D4, 14);
  outportb (0x3D5, temp >> 8);
  outportb (0x3D4, 15);
  outportb (0x3D5, temp);
}

/* Clears the screen */
void cls ()
{
  unsigned blank;
  int i;

  /* Again, we need the 'short' that will be used to
   *  represent a space with color */
  blank = 0x20 | (attrib << 8);

  /* Sets the entire screen to spaces in our current
   *  color */
  for (i = 0; i < 25; i++)
    memsetw (textmemptr + i * 80, blank, 80);

  csr_x = 0;
  csr_y = 0;
  move_csr ();
}

/* Puts a single character on the screen */
void
putch (unsigned char c)
{
  unsigned short *where;
  unsigned att = attrib << 8;

  if (c == 0x08)
    {
      if (csr_x != 0)
	csr_x--;
    }

  else if (c == 0x09)
    {
      csr_x = (csr_x + 8) & ~(8 - 1);
    }

  else if (c == '\r')
    {
      csr_x = 0;
    }

  else if (c == '\n')
    {
      csr_x = 0;
      csr_y++;
    }

  else if (c >= ' ')
    {
      where = textmemptr + (csr_y * 80 + csr_x);
      *where = c | att;		/* Character AND attributes: color */
      csr_x++;
    }

  if (csr_x >= 80)
    {
      csr_x = 0;
      csr_y++;
    }

  scroll ();
  move_csr ();
}

void putsnocr (unsigned char *text)
{
  int i;
  for (i = 0; i < strlen ((char *)text); i++)
    putch (text[i]);
}

void
puts (unsigned char *text)
{
  putsnocr (text);
  putch ('\n');
}

void
printxy (unsigned char *text, int x, int y)
{
  unsigned short *memptr = textmemptr + ((y * 80) + x);
  while (*text != 0)
    {
      *memptr = *text | (0x0F << 8);
      memptr++;
      text++;
    }
}

void
settextcolor (unsigned char forecolor, unsigned char backcolor)
{
  /* Top 4 bytes are the background, bottom 4 bytes
   *  are the foreground color */
  attrib = (backcolor << 4) | (forecolor & 0x0F);
}

void
init_video (void)
{
  textmemptr = (unsigned short *) 0xC00B8000;
  cls ();
  kprint ("Video initialized\n");
}

